﻿using System;
using System.Collections.Generic;

namespace Guesshoo.TrainExpress.Extensions
{
	public static class EnumerableExtensions
	{
		public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> pSource,
		                                           Func<TSource, TKey> pSelector) {
			return pSource.MinBy(pSelector, Comparer<TKey>.Default);
		}

		public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> pSource,
		                                           Func<TSource, TKey> pSelector,
		                                           IComparer<TKey> pComparer) {
			pSource.ThrowIfNull("source");
			pSelector.ThrowIfNull("pSelector");
			pComparer.ThrowIfNull("pComparer");
			using (IEnumerator<TSource> sourceIterator = pSource.GetEnumerator()) {
				if (!sourceIterator.MoveNext()) {
					throw new InvalidOperationException("Sequence was empty");
				}
				TSource min = sourceIterator.Current;
				TKey minKey = pSelector(min);
				while (sourceIterator.MoveNext()) {
					TSource candidate = sourceIterator.Current;
					TKey candidateProjected = pSelector(candidate);
					if (pComparer.Compare(candidateProjected, minKey) < 0) {
						min = candidate;
						minKey = candidateProjected;
					}
				}
				return min;
			}
		}
	public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> pSource,
		                                           Func<TSource, TKey> pSelector) {
		return pSource.MaxBy(pSelector, Comparer<TKey>.Default);
	}

		public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> pSource,
		                                           Func<TSource, TKey> pSelector,
		                                           IComparer<TKey> pComparer) {
			pSource.ThrowIfNull("source");
			pSelector.ThrowIfNull("pSelector");
			pComparer.ThrowIfNull("pComparer");
			using (IEnumerator<TSource> sourceIterator = pSource.GetEnumerator()) {
				if (!sourceIterator.MoveNext()) {
					throw new InvalidOperationException("Sequence was empty");
				}
				TSource max = sourceIterator.Current;
				TKey maxKey = pSelector(max);
				while (sourceIterator.MoveNext()) {
					TSource candidate = sourceIterator.Current;
					TKey candidateProjected = pSelector(candidate);
					if (pComparer.Compare(candidateProjected, maxKey) > 0) {
						max = candidate;
						maxKey = candidateProjected;
					}
				}
				return max;
			}
		}
	}
}